%w(rubygems daemons).each { |f| require f }

class Numeric
  def seconds; self; end
  def minutes; self * 60; end
  def hours;  self * 3600; end
  def ago; Time.now - self; end
  def elapsed_since?(t); t < self.ago; end
end

class Date
   def last_hour?
      Time.now - self < 60.minutes
   end
end



class Project
   attr_accessor :actions
   def initialize; @actions = []; end
   def <<(action); @actions << action; end
   
   def next_action!
      loop {
         break unless actions.reject! {|x| send(x[0], *x[1,-1]) }
      }
   end
end



class Friend
   attr_accessor :name, :waiting_for, :bot,
      :last_seen, :seen_count, 
      :last_used, :used_count, 
      :last_responded, :responded_count
   alias_method :to_str, :name
   
   def initialize(name)
      super()
      @name = name
      @seen_count = @responded_count = @used_count = 0
      @waiting_for = []
      @last_seen = @last_used = @last_responded = Time.now
   end
   
   def seen!; @last_seen = Time.now; @seen_count += 1; end
   def used!; @last_used = Time.now; @asked_count += 1; end
   def done!; responded!; context = nil; end
   
   def waiting_for?; not @waiting_for.empty?; end

   def tell(x, response_requested=nil)
      bot.tell(name, x)
      if response_requested
         used!
         @waiting_for << response_requested
      end
   end
   
   def responded!(response)
      seen!
      if @waiting_for.reject! {|x| x[0].send(x[1], self, *x[2,-1]) }
         @last_responded = Time.now
         @responded_count += 1
      end
   end
   
   def usable?
      last_seen < 60.minutes.ago and ! waiting_for? # and last_used > 20.minutes.ago
   end

   def trusted?
      false
   end   
end


Friends = Hash.new { |h, k| h[k] = Friend.new(k) }
def Friends.for_assignment(count)
   g = Friends.values.select{|f| f.usable? }.sort_by{|f| f.last_used || 0 }
   return false if g.size < count
   return g.first(count)
end


module Programmable
   def every(freq, &block)
      (@tasks ||= []) << [freq, nil, block]
   end

   def heartbeat
      @tasks.collect{|t|t[0]}.min
   end

   def run
      @tasks.each do |t|
         freq, tstamp, block = *t
         if !tstamp or Time.now - tstamp > freq
            t[1] = Time.now
            block.call
         end
      end
   end

   def daemonize
      begin
         Daemons.run_proc "#{self.class} daemon", :backtrace => true, :dir_mode => :system, :log_output => true do
            begin
               loop { run; sleep heartbeat }
            rescue Exception => e
               puts "#{e} (#{e.class})\n#{e.backtrace.join("\n")}"
            end
         end
      rescue Exception => e
         puts "#{e} (#{e.class})\n#{e.backtrace.join("\n")}"
      end         
   end

   def log(str, severity=:info)
     if @logger
       @logger.send(severity, str)
     else
       STDERR.puts(str)
     end
   end   
end



class Robot
   include Programmable
   
   def receive(method, sender, message)
      f = Friends[sender]
      f.bot = self
      unless f.waiting_for? and f.responded!(message)
         greeting(method, f, message)
      end
   end      
end
